package com.metal.meta.service.form;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.metal.meta.exception.ResponseVo;
import com.metal.meta.util.MongoDBUtil;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.client.FindIterable;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.result.UpdateResult;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.sql.SqlUtil;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.regex.Pattern;

/**
 * @ClassName FormDataService
 * @Description 这个类的主要作用，是封装对mongodb的操作，同时，对前端提交的数据进行解析、包装并调用mongodb api进行CRUD
 * @Author liukun
 * @Date 2022/10/17 08:58
 */
@Slf4j
@Service
public class FormDataService {

    public static int addSave(Object data) {
        String collection = ServletUtils.getParameter("domain");
        JSONObject jsonObject = (JSONObject) JSONObject.toJSON(data);
        jsonObject.put("uuid", UUID.fastUUID().toString());
        jsonObject.put("add_time", DateUtil.now());
        jsonObject.put("update_time", DateUtil.now());
        jsonObject.put("del_flag", 0);
        jsonObject.remove("params");
        Document insertDoc = Document.parse(jsonObject.toJSONString());
        MongoDBUtil.getCollection(collection).insertOne(insertDoc);
        return 1;
    }


    public static TableDataInfo list(Object customer1) {
        /**
         * @see com.ruoyi.common.utils.PageUtils#startPage
         */
        String collection = ServletUtils.getParameter("domain");

        PageDomain pageDomain = TableSupport.buildPageRequest();
        Integer pageNum = pageDomain.getPageNum();
        Integer pageSize = pageDomain.getPageSize();
        String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
//        Boolean reasonable = pageDomain.getReasonable();
//        PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);

        JSONObject queryJsonObj = (JSONObject) JSONObject.toJSON(customer1);
        BasicDBObject query= new BasicDBObject();
        // 普通查询字段，用like查询
        for (String key : queryJsonObj.keySet()) {
            if (queryJsonObj.containsKey(key) && StringUtils.hasLength(queryJsonObj.getString(key)) && !key.equalsIgnoreCase("params")) {
                query.put(key, Pattern.compile(queryJsonObj.getString(key), Pattern.CASE_INSENSITIVE));
            }
        }
        query.put("del_flag", 0);
        // 构造排序 bson
        JSONObject sortJson = new JSONObject();
        sortJson.put("add_time", -1);

        // 当前页数据查询
        FindIterable<Document> documents = MongoDBUtil.getCollection(collection)
                .find(query)
                .sort(Document.parse(sortJson.toJSONString()))
                .skip((pageNum - 1) * pageSize)
                .limit(pageSize);
        List<JSONObject> resultList = MongoDBUtil.resultList(documents);
        // 总数量查询
        long total = MongoDBUtil.getCollection(collection).countDocuments(query);

        TableDataInfo rspData = new TableDataInfo();
        rspData.setCode(0);
        rspData.setRows(resultList);
        rspData.setTotal(total);

        return rspData;
    }

    public static JSONObject selectByUuid(String collection, String uuid) {
        BasicDBObject query= new BasicDBObject();
        query.put("uuid", uuid);
        FindIterable<Document> documents = MongoDBUtil.getCollection(collection).find(query);
        JSONObject resultJson = MongoDBUtil.resultOne(documents);
//        return jsonObj2Map(resultJson);
        return resultJson;
    }

    public static int editSave(Object params) {
        String collection = ServletUtils.getParameter("domain");
        // 提取uuid
        JSONObject inputJsonObject = (JSONObject) JSONObject.toJSON(params);
        inputJsonObject.put("update_time", DateUtil.now());
        String uuid = inputJsonObject.getString("uuid");
        JSONObject dbJsonObject = MongoDBUtil.queryOneByUuid(collection, uuid);
        updateCustomerByUuid(collection, dbJsonObject.getString("uuid"), inputJsonObject, dbJsonObject, null);
        return 1;
    }

    public static JSONObject updateCustomerByUuid(String collection,
                                                  String uuid,
                                                  JSONObject inputJsonDataPayload,
                                                  JSONObject dbCustomer,
                                                  JSONObject formCoordinate) {
        inputJsonDataPayload = buildNewUpdateJson(inputJsonDataPayload, dbCustomer, formCoordinate);
        MongoDBUtil.updateSelectiveByUuid(collection, uuid, inputJsonDataPayload.toJSONString());
        return inputJsonDataPayload;
    }

    /**
     * 组装客户信息
     *
     * @param inputJsonDataPayload
     * @param dbCustomer
     * @return
     */
    public static JSONObject buildNewUpdateJson(JSONObject inputJsonDataPayload, JSONObject dbCustomer, JSONObject formCoordinate) {

        BasicDBObject query = new BasicDBObject();
        query.put("type", "table");
        query.put("del_flag", 0);
        if (formCoordinate!=null && formCoordinate.containsKey("namespace")) {
            query.put("namespace", formCoordinate.getString("namespace"));
        }
        if (formCoordinate!=null && formCoordinate.containsKey("system")) {
            query.put("system", formCoordinate.getString("system"));
        }
        if (formCoordinate!=null && formCoordinate.containsKey("form")) {
            query.put("form", formCoordinate.getString("form"));
        }
        FindIterable<Document> documents = MongoDBUtil.getCollection("configure").find(query);

        //2.遍历配置，获取客户表单中table对应的key和unique,对比入参和数据库中客户xx信息。
        for (Document doc : documents) {
            //获取配置里table的主键字段,如:共借人身份证  co_idcard
            String uniqueColumn = ((ArrayList)doc.get("children_unique")).get(0).toString();
            //获取配置里table的字段,如:共借人信息  co_borrower_information
            String tableColumn = doc.getString("column_name");
            //JSONPath根据tableColumn取出入参中所有的满足条件的xx信息，返回是一个两层list,因客户信息每个key都不同，此处取第一个条即可。
            Object paramTableColumn = JsonPath.read(inputJsonDataPayload, "$.." + tableColumn);
            Map<Object, Object> paramUniqueMap = getList2Map(uniqueColumn, paramTableColumn);
            //获取数据库中客户xx信息list，转换成map,主键为key,一条xx信息为value
            Object dbTableColumn = JsonPath.read(dbCustomer, "$.." + tableColumn);
            Map<Object, Object> dbUniqueMap = getList2Map(uniqueColumn, dbTableColumn);
            List customerList =  new ArrayList();
            //合并入参和数据库客户XX信息主键，遍历主键，进行数据比对
            Set<Object> keySet = new HashSet<>();
            keySet.addAll(paramUniqueMap.keySet());
            keySet.addAll(dbUniqueMap.keySet());
            keySet.forEach(k->{
                //如果客户xx信息数据库和入参都存在，比对入参信息中每个字段值，为空取数据库的值，反之取入参中字段值，封装成新的记录，添加到客户xx信息列表中
                if ((!ObjectUtils.isEmpty(dbUniqueMap.get(k)))&&(!ObjectUtils.isEmpty(paramUniqueMap.get(k)))) {
                    Map dbItem = JSONObject.parseObject(dbUniqueMap.get(k).toString(), Map.class);
                    Map paramItem =JSONObject.parseObject(paramUniqueMap.get(k).toString(), HashMap.class);
                    dbItem.putAll(paramItem);
                    /*Set<String> paramFields = paramItem.keySet();
                    for (String field : paramFields) {
                        if (!ObjectUtils.isEmpty(paramItem.get(field))) {
                            dbItem.put(field, paramItem.get(field));
                        }
                    }*/
                    customerList.add(dbItem);
                };
                //如果数据库存在，入参不存在，添加到客户xx信息列表中
                if((!ObjectUtils.isEmpty(dbUniqueMap.get(k)))&&ObjectUtils.isEmpty(paramUniqueMap.get(k))){
                    customerList.add(dbUniqueMap.get(k));
                };
                //如果入参存在，数据库不存在，添加到客户xx信息列表中
                if(ObjectUtils.isEmpty(dbUniqueMap.get(k))&&(!ObjectUtils.isEmpty(paramUniqueMap.get(k)))){
                    customerList.add(paramUniqueMap.get(k));
                }
            });
            //替换入参中字段类型为table信息
            DocumentContext parse = JsonPath.parse(inputJsonDataPayload);
            parse.set("$.." + tableColumn,customerList);
        }

        // 这里，如果传来的参数是空字符串，并且数据库中的数据不是空字符串，那么以数据库中的数据为准.
        for (String key: inputJsonDataPayload.keySet()) {
            if (inputJsonDataPayload.get(key) instanceof String) {
                if (dbCustomer.containsKey(key) && !StringUtils.isEmpty(dbCustomer.getString(key)) && StringUtils.isEmpty(inputJsonDataPayload.getString(key))) {
                    inputJsonDataPayload.put(key, dbCustomer.get(key));
                }
            }
        }

        return inputJsonDataPayload;
    }

    /**
     * list 转换成map
     * @param uniqueColumn  配置表中主键
     * @param tableColumn  具体数据list
     * @return
     */
    private static Map<Object, Object> getList2Map(String uniqueColumn, Object tableColumn) {
        Map<Object, Object> paramUniqueMap = new HashMap<>();
        if (!ObjectUtils.isEmpty(tableColumn) && (!ObjectUtils.isEmpty(JSONObject.parseArray(String.valueOf(tableColumn)).get(0)))) {
            JSONArray paramTable = JSONObject.parseArray(String.valueOf(JSONObject.parseArray(String.valueOf(tableColumn)).get(0)));
            //将入参里的table数据转换成map,主键为key
            paramTable.forEach(param -> {
                String s = JSON.toJSONString(param);
                Map paramMap = JSON.parseObject(s, Map.class);
                paramUniqueMap.put(paramMap.get(uniqueColumn), param);
            });
        }
        return paramUniqueMap;
    }

    private static Map<String, Object> jsonObj2Map(JSONObject jsonObject) {
        Map<String, Object> userMap = new HashMap<>();
        //循环转换
        for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
            userMap.put(entry.getKey(), entry.getValue());
        }
        return userMap;
    }

    public static int deleteByUuids(String ids) {
        String collection = ServletUtils.getParameter("domain");

        BasicDBObject query= new BasicDBObject();
        BasicDBList values = new BasicDBList();
        values.addAll(Arrays.asList(ids.split(",")));
        query.put("uuid", new BasicDBObject("$in",values));

        JSONObject updateJson = new JSONObject();
        updateJson.put("del_flag", 1);
        Document doc2 = new Document("$set", Document.parse(updateJson.toJSONString()));
        UpdateOptions up = new UpdateOptions();
        up.upsert(false);
        UpdateResult a = MongoDBUtil.getCollection(collection).updateMany(query, doc2, up);
        return 1;
    }

    /**
     * 将提交来的JsonObject对象存储到collection中
     * @param collection
     * @param jsonObject
     */
    public static void saveJson2Collection(String collection, JSONObject jsonObject) {
        // 自动生成uuid, add_time, update_time, del_flag
        jsonObject.put("uuid", UUID.fastUUID().toString());
        jsonObject.put("add_time", DateUtil.now());
        jsonObject.put("update_time", DateUtil.now());
        jsonObject.put("del_flag", 0);
        // 准备执行insert
        Document insertDoc = Document.parse(jsonObject.toJSONString());
        MongoDBUtil.getCollection(collection).insertOne(insertDoc);
        log.info("保存数据到【{}】成功。数据为【{}】", collection, jsonObject.toJSONString());
    }


    public static void jsonUpdate(String collection, String uuid, JSONObject inputJsonDataPayload, JSONObject formCoordinate) {
        // 提取uuid
        JSONObject dbJsonObject = MongoDBUtil.queryOneByUuid(collection, uuid);
        updateCustomerByUuid(collection, uuid, inputJsonDataPayload, dbJsonObject, formCoordinate);
    }

    public static List<JSONObject> jsonQuery(String collection, JSONObject inputJsonObject) {
        JSONObject queryJsonObj = new JSONObject();
        for (String key: inputJsonObject.keySet()) {
            queryJsonObj.put(key, inputJsonObject.getString(key));
        }
        Document queryDoc = Document.parse(queryJsonObj.toJSONString());
        FindIterable<Document> documents = MongoDBUtil.getCollection(collection).find(queryDoc);
        return MongoDBUtil.resultList(documents);

    }
}
